-- NewAirShipM
-- Create by chendh
-- 新版天空之城模块

module("NewAirShipM", package.seeall);

-- 所有的配置信息
local airShipGuideTable = {};
local airShipTable      = {};
local airShipDeckTable  = {};
local airShipStrengthenTable    = {};
local airShipMaterialTable      = {};

local ATTRIB_PATH       = "airship_guide";
local isNew             = false;
local AIRSHIP_PATH      = "new_airships";
local GROUP_DEVICE_NUM  = 4; -- 每组改造项所拥有的装置数量
local MAX_STAR_NUM      = 5; -- 飞艇最大星级

local ringsOwners = {};

-- 当前界面选中飞艇id
local selectedShipId;

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    airShipGuideTable = {};
    airShipTable      = {};
    airShipDeckTable  = {};

    -- 转换下airship信息表
    local airShip_CSV = get_new_airship_CSV();
    for i = 1, #airShip_CSV do
        airShipTable[airShip_CSV[i].id] = airShip_CSV[i];

        -- 光环ID到飞艇映射表
        ringsOwners[airShip_CSV[i].rings_skill] = airShip_CSV[i].id;
    end

    -- 转换下sky_city_guide信息表
    local airShipGuide_CSV = get_sky_city_guide_CSV();
    for i = 1, #airShipGuide_CSV do
        airShipGuideTable[airShipGuide_CSV[i].id] = airShipGuide_CSV[i];
    end

    -- 转换下airship_deck信息表
    local airShipDeck_CSV = get_airship_deck_CSV();
    for i = 1, #airShipDeck_CSV do
        airShipDeckTable[airShipDeck_CSV[i].id] = airShipDeck_CSV[i];
    end

    -- 转换下airship信息表
    local airShipStrengthen_CSV = get_airship_strengthen_CSV();
    for i = 1, #airShipStrengthen_CSV do
        airShipStrengthenTable[airShipStrengthen_CSV[i].id] = parseAirShipStrengthen(airShipStrengthen_CSV[i]);
    end

    -- 转换下airship信息表
    local airShipMaterial_CSV = get_airship_material_CSV();
    for i = 1, #airShipMaterial_CSV do
        airShipMaterialTable[airShipMaterial_CSV[i].class_id] = airShipMaterial_CSV[i];
    end

    airShip_CSV = {};
    airShipGuide_CSV = {};
    airShipDeck_CSV = {};
    airShipStrengthen_CSV = {};
    airShipMaterial_CSV = {};
end

-- 模块初始化
function init()
    loadCsv();
end

-- 解析飞艇强化配置表
function parseAirShipStrengthen(row)
    local ret = {};
    for k, v in pairs(row) do
        if type(k) == "string" and string.startWith(k, "lv") then
            -- 多种消耗之间用'+'分隔
            local arr = string.explode(v, "+");

            -- 配置的格式为：'lv%d'
            local level = tonumber(string.sub(k, 3, -1));
            ret[level] = {};
            for _, strCost in pairs(arr) do
                local temp = string.explode(string.sub(strCost, 3, -2), ",");
                if string.startWith(strCost, "1(") then
                    -- 物品消耗
                    table.insert(ret[level], { 1, tonumber(temp[1]), tonumber(temp[2]) });
                elseif string.startWith(strCost, "2(") then
                    -- 属性消耗
                    table.insert(ret[level], { 1, temp[1], tonumber(temp[2]) });
                else
                    assert(false, "暂不支持的格式");
                end
            end
        end
    end

    return ret;
end

-- 检索信息
function query(id, path)
    local m = airShipTable[id]

    if not m or path == "" or not path then
        return m;
    end

    return m[path];
end

-- 检索信息
function queryGuide(id, path)
    local m = airShipGuideTable[id]

    if not m or path == "" or not path then
        return m;
    end

    return m[path]
end

-- 检索信息
function queryDeck(id, path)
    local m = airShipDeckTable[id]

    if not m or path == "" or not path then
        return m
    end

    return m[path];
end

-- 检索强化消耗信息
function queryStrengthen(id, path)
    local m = airShipStrengthenTable[id]

    if not m or path == "" or not path then
        return m
    end

    return m[path];
end

-- 检索飞艇强化材料信息
function queryMaterial(id, path)
    local m = airShipMaterialTable[id]

    if not m or path == "" or not path then
        return m
    end

    return m[path];
end

-- 设置是否使用新版飞艇系统
function setIsNew(data)
    isNew = data;
end

-- 判断是否使用新版飞艇系统
function isOpen()
    return isNew;
end

-- 是否结束了指引
function isFinishGuide()
    return getGuideProgress() >= table.maxKey(airShipGuideTable);
end

-- 获取指引进度
function getGuideProgress()
    return ME.user.dbase:query(ATTRIB_PATH) or 0;
end

-- 设置指引进度
function setGuideProgress(progress)
    local cur_progress = getGuideProgress();

    if cur_progress >= progress then
        return false;
    end

    -- 如果还未开放此进度
    if AIRSHIP_GUIDE_LIMIT and progress > AIRSHIP_GUIDE_LIMIT then
        doAlert(getLocStr("unopen_function_tip"));
        return false;
    end

    for i = cur_progress + 1, progress do
        -- 执行奖励
        local configBonus = queryGuide(i, "bonus");

        local prefix = "formula_";
        local bonusList = {};

        if type(configBonus) == "table" then
            bonusList = configBonus;
        elseif type(configBonus) == "string" and string.startWith(configBonus, prefix) then
            -- 配置了公式，调用公式计算
            local formulaName = string.sub(configBonus, string.len(prefix) + 1);
            bonusList = FormulaM.invoke(formulaName);
        end

        if type(bonusList) == "table" and #bonusList > 0 then
            for _, bonus in ipairs(bonusList) do
                BonusM.doBonus(bonus, "airship_guide");
            end
        end

        local dbase = queryGuide(i, "dbase") or {};
        local special = dbase["special"];
        local petId = dbase["pet"];

        if special and petId then
            local pet = PetM.getMyPet(petId);

            -- 修改专属
            pet.special = special;
            pet.dbase:set("special", special);

            -- TODO: 这里不需要刷新玩家属性，因为是在迷宫外面，专属不起效
        end

        -- 修复飞船
        if dbase["repair"] then
            repair();
        end
    end

    ME.user.dbase:set(ATTRIB_PATH, progress);

    Operation.cmd_set_airship_guide_progress(progress);
end

-- 修理飞艇的冈布奥数量要求
function repairPetNumLimit()
    return 15;
end

-- 修理飞艇的金币消耗
function repairCostMoney()
    return 500000;
end

-- 判断玩家当前是否可以修理飞船
function canRepair()
    -- 冈布奥至少要10头
    if #ME.user:getAllPetIds() < repairPetNumLimit() then
        return false;
    end

    if ME.user.dbase:query("money") < repairCostMoney() then
        return false;
    end

    return true;
end

-- 修理飞船
function repair()
    if not canRepair() then
        return false;
    end

    ME.user:costAttrib("money", repairCostMoney());

    EventMgr.fire(event.SKY_CITY_REPAIR);

    -- Operation.cmd_airship_repair();
end

-- 获取所有的飞船
function getAllAirShips()
    local shipList = {};
    for id, _ in ipairs(airShipTable) do
        table.insert(shipList, id);
    end

    return shipList;
end

-- 获取所有拥有芯片或者已建造的飞船
function getCanDrawAirships()
    local ownList = {};
    local notOwnList = {};

    -- 获取所有飞船
    local allShips = getAllAirShips();
    for shipId, _ in ipairs(allShips) do
        if hasOwnShip(shipId) then
            -- 如果已拥有
            table.insert(ownList, shipId);
        elseif getShipChipNum(shipId) > 0 then
            -- 如果有它的芯片
            table.insert(notOwnList, shipId);
        end
    end

    -- 合并table
    return table.append(ownList, notOwnList);
end

-- 获取飞船的芯片数量
function getShipChipNum(id)
    local chipId = query(id, "chip_id");
    local num = ItemM.getAmount(ME.user, chipId);
    if num > 0 then
        return num;
    end
    return 0;
end

-- 获取玩家所拥有的飞艇
function getOwnAirShips()
    local ownShips = {};
    local airships = ME.user.dbase:query(AIRSHIP_PATH) or {};
    for shipId, _ in pairs(airships) do
        table.insert(ownShips, shipId);
    end
    return ownShips;
end

-- 获取拥有的飞艇数量
function getOwnAirShipNum()
    local ownShips = getOwnAirShips();
    return #table.keys(ownShips);
end

-- 当前是否拥有飞艇id
function hasOwnShip(id)
    local airships = ME.user.dbase:query(AIRSHIP_PATH) or {};
    if not airships[id] then
        return false;
    end

    return true;
end

-- 计算强化加成
function calcStengthenAttrib(shipId, data, strengthen, campId)
    local prop;

    -- 强化的加成
    local strengthenProp = {};
    if #strengthen > 0 then
        for _, id in ipairs(strengthen) do
            local material = airShipStrengthenTable[shipId][id][1];
            local materialId = material[2];
            prop = airShipMaterialTable[materialId]["prop"];

            for key, value in pairs(prop) do
                if table.indexOf({"fire", "armor", "luck", "speed", "fight_ability"}, key) > 0 then
                    -- 基本属性
                    data[key] = value + data[key];
                    if not strengthenProp[key] then
                        strengthenProp[key] = 0;
                    end
                    strengthenProp[key] = value + strengthenProp[key];
                elseif key == "camp_attrib" then
                    -- 阵营偏向属性
                    for requestCamp, campProp in pairs(value) do
                        if campId == requestCamp then
                            -- 必须是符合要求的阵营才起效
                            for campKey, campValue in pairs(campProp) do
                                if table.indexOf({"fire", "armor", "luck", "speed", "fight_ability"}, campKey) > 0 then
                                    -- 基本属性
                                    data[campKey] = campValue + data[campKey];
                                    if not strengthenProp[campKey] then
                                        strengthenProp[campKey] = 0;
                                    end
                                    strengthenProp[campKey] = campValue + strengthenProp[campKey];
                                else
                                    if type(campValue) == "number" then
                                        if not strengthenProp[campKey] then
                                            strengthenProp[campKey] = 0;
                                        end
                                        strengthenProp[campKey] = campValue + strengthenProp[campKey];
                                    elseif type(campValue) == "table" then
                                        if not strengthenProp[campKey] then
                                            strengthenProp[campKey] = {};
                                        end
                                        table.insert(strengthenProp[campKey], campValue);
                                    else
                                        assert(false, "暂不支持的格式");
                                    end
                                end
                            end
                        end
                    end
                else
                    if type(value) == "number" then
                        if not strengthenProp[key] then
                            strengthenProp[key] = 0;
                        end
                        strengthenProp[key] = value + strengthenProp[key];
                    elseif type(value) == "table" then
                        if not strengthenProp[key] then
                            strengthenProp[key] = {};
                        end
                        table.insert(strengthenProp[key], value);
                    else
                        assert(false, "暂不支持的格式");
                    end
                end
            end
        end
    end

    return data, strengthenProp;
end

-- 获取玩家飞船的信息
-- @param petList 用于计算飞艇信息的冈布奥列表，如果没有传入，则默认使用当前出战的冈布奥列表
function getUserAirShipInfo(shipId, petList, robotList, campId)
    local airships = ME.user.dbase:query(AIRSHIP_PATH) or {};
    local airshipInfo = airships[shipId];
    local airshipConfig = query(shipId);

    if type(airshipInfo) ~= "table" or type(airshipConfig) ~= "table" then
        return {};
    end

    local attribMap = {
        ["init_fire"]       = "fire",
        ["init_armor"]      = "armor",
        ["init_speed"]      = "speed",
        ["init_luck"]       = "luck",
        ["init_rank"]       = "rank",
    };

    -- 所有出战宠物
    local army = petList or SkyCombatM.getSkyArmy() or {};
    local pets = {};
    for _, classId in pairs(army) do
        table.insert(pets, ME.user.pets[classId]);
    end

    -- 机器人加成
    local camp = ME.user.dbase:query("activedSkyGroup")
    local robotMap = robotList or SkyRobotM.getRobotsByCamp(camp);
    local robotAddon = SkyRobotM.getRobotListAddon(robotMap);

    local prop;
    local addon;
    local data = {};
    local propId;
    for key, value in pairs(attribMap) do
        addon = 0;
        -- 出战飞艇能力加成
        if getActiveAirShip() == shipId then
            -- 天空战飞艇能力加成
            prop = PropM.combine(ME.user, "sky_attrib", value);
            addon = PropM.apply(prop, addon);

            -- 天空战飞艇能力加成
            prop = PropM.combine(ME.user, "all_sky_attrib", 1);
            addon = PropM.apply(prop, addon);

            -- 宠物装备加成
            propId = PropM.getPropId("attrib2");
            prop = PropM.combineProps(pets, propId, value);
            addon = PropM.apply(prop, addon);

            -- 机器人加成
            addon = addon + (robotAddon[value] or 0);
        end

        data[value] = airshipConfig[key] +
               (airshipInfo[value] or 0) + addon;

        -- 被动秘宝加成
        data[value] = SkyShipSkillM.calcShipAttrib(value, data[value], army);
    end

    local strengthen = airshipInfo["strengthen"] or {};
    local strengthenProp = {};
    data, strengthenProp = calcStengthenAttrib(shipId, data, strengthen, campId or camp);

    data["strengthen_prop"] = strengthenProp;

    -- 天空之城附加属性
    local skyProp = SkyPropM.refreshSkyProp(ME.user, army);
    for _, attrib in ipairs({"fire", "armor", "speed", "luck", }) do
        local addon = SkyPropM.calcAddonByData({["sky_prop"] = skyProp}, attrib, data[attrib]);
        data[attrib] = data[attrib] + addon;
    end

    return data;
end

-- 获取玩家飞船的信息（不带属性加成）
function getUserAirShipInfoWithoutProp(shipId)
    local airships = ME.user.dbase:query(AIRSHIP_PATH) or {};
    local airshipInfo = airships[shipId];
    local airshipConfig = query(shipId);

    if type(airshipInfo) ~= "table" or type(airshipConfig) ~= "table" then
        return nil;
    end

    local attribMap = {
        ["init_fire"]       = "fire",
        ["init_armor"]      = "armor",
        ["init_speed"]      = "speed",
        ["init_luck"]       = "luck",
        ["init_rank"]       = "rank",
    };

    local data = {};
    for key, value in pairs(attribMap) do
        data[value] = airshipConfig[key] + (airshipInfo[value] or 0);
    end

    local campId = ME.user.dbase:query("activedSkyGroup");
    local strengthen = airshipInfo["strengthen"] or {};
    local strengthenProp = {};
    data, strengthenProp = calcStengthenAttrib(shipId, data, strengthen, campId);

    data["strengthen_prop"] = strengthenProp;

    return data;
end

-- 获取飞艇的火炮等级
function getCannonLevel(shipId)
    local shipInfo = getUserAirShipInfoWithoutProp(shipId) or {};
    local strengthenProp = shipInfo["strengthen_prop"];
    return strengthenProp and strengthenProp["cannon_level"] or 0;
end

-- 获取飞艇主炮技能id
function getAirshipSKill(shipId)
    local physicSkill = query(shipId, "physic_skill");
    local cannonLevel = getCannonLevel(shipId);

    return SkyShipSkillM.getSkillByLevel(physicSkill, cannonLevel);
end

-- 合成材料
function compoundMaterial(classId)
    -- 获取合成规则
    local rule = CompoundM.getCompoundRule(classId);
    if not rule then
        return false;
    end

    -- 合成
    Operation.cmd_do_compound(rule);

    return true;
end

-- 合成强化装置
function compoundDevice(shipId, level)
    if isMaterialEnough(shipId, level) ~= true then
        -- 材料不足
        return false;
    end

    local device = getStrengthenDevice(shipId, level);
    local deviceId = device[2];

    -- 获取合成规则
    local rule = CompoundM.getCompoundRule(deviceId);
    if not rule then
        return false;
    end

    -- 合成
    Operation.cmd_do_compound(rule);

    return true;
end

-- 获得飞船的光环技能
function getAirShipSkillId(shipId)
    return query(shipId, "rings_skill");
end

-- 获得飞船的光环等级
function getRingsLevel(shipId)
    -- 当前星级
    local curRank = getAirShipRank(shipId);
    local level;

    if hasOwnShip(shipId) then
        level = curRank;
    else
        level = query(shipId, "init_rank");
    end

    return level;
end

-- 获得飞船的强化记录
function getStrengthenRecord(id)
    local airships = ME.user.dbase:query(AIRSHIP_PATH) or {};
    local airshipInfo = airships[id];
    return airshipInfo and airshipInfo["strengthen"] or nil;
end

-- 获得飞船的强化次数
function getAirShipStrengtenTimes(id)
    local airships = ME.user.dbase:query(AIRSHIP_PATH) or {};
    local airshipInfo = airships[id];
    local strengthen = airshipInfo and airshipInfo["strengthen"] or nil;

    return type(strengthen) == "table" and #strengthen or 0;
end

-- 获得飞艇的阶位(星级)
function getAirShipRank(id)
    local airships = ME.user.dbase:query(AIRSHIP_PATH) or {};
    local airshipInfo = airships[id];
    local rank = airshipInfo and airshipInfo["rank"] or 0;
    return rank + query(id, "init_rank");
end

-- 获取当前激活的飞艇
function getActiveAirShip()
    local shipId = ME.user.dbase:query("active_airship");

    if not shipId or shipId <= 0 then
        return 1;
    else
        return shipId;
    end
end

-- 获取最近一次修理飞艇的时间
function getLastRepairTime()
    return ME.user.dbase:query("last_repair_airship_time", 0);
end

-- 获取最大可强化等级
function getMaxStrengthenLevel(shipId)
    return FormulaM.invoke("CALC_AIRSHIP_MAX_STRENGTHEN_LEVEL", shipId);
end

-- 获取当前阶段最大可强化等级
function getCurMaxStrengthenLevel(shipId)
    local showGroup = getShowGroup(shipId);
    return showGroup * 4;
end

-- 当前可以显示的组数
function getShowGroup(shipId)
    local rank = getAirShipRank(shipId);
    return rank;
end

-- 获取最大阶级
function getMaxRank(shipId)
    return FormulaM.invoke("CALC_AIRSHIP_MAX_RANK_LEVEL", shipId);
end

-- 获取强化消耗
function getStrengthenCost(shipId, level)
    if not airShipStrengthenTable[shipId] then
        return nil;
    end

    return airShipStrengthenTable[shipId][level];
end

-- 获取强化需要的装置
function getStrengthenDevice(shipId, level)
    local ship = hasOwnShip(shipId);
    if not ship then
        -- 玩家没有该飞艇
        return nil;
    end

    if not level then
        level = getAirShipStrengtenTimes(shipId);
    end

    local cost = getStrengthenCost(shipId, level);
    if not cost then
        -- 消耗不存在，可能是没有配置
        return nil;
    end

    -- 默认返回第一个消耗（目前仅一种消耗）
    return cost[1];
end

-- 强化需要的装置是否足够
function isDeviceEnough(shipId, level)
    local device = getStrengthenDevice(shipId, level);
    if not device then
        -- 获取不到食物配置
        return false;
    end

    if ItemM.getAmount(ME.user, device[2]) < device[3] then
        return false;
    end

    return true;
end

-- 合成强化装置所需要的材料是否足够
function isMaterialEnough(shipId, level)
    local materialId;
    local device = getStrengthenDevice(shipId, level);
    if not device then
        -- 获取不到食物配置
        return false;
    end

    materialId = device[2];
    if not materialId then
        return false;
    end

    -- 获取合成规则
    local rule = CompoundM.getCompoundRule(materialId);
    if not rule then
        return false;
    end

    -- 判断能否合成
    return CompoundM.canDoCompound(rule);
end

function canMaterialCompound(classId)
    -- 获取合成规则
    local rule = CompoundM.getCompoundRule(classId);
    if not rule then
        return false;
    end

    -- 判断能否合成
    return CompoundM.canDoCompound(rule);
end

-- 获取飞艇的阵营偏向
function getAirShipCamp(shipId)
    return query(shipId, "camp");
end

------------------------------
-- 计算根据光环技能Id查询飞艇
-- @param skillId  光环Id
function getAirshipByRingsId(skillId)
    local baseSkill = SkillM.findBaseSkill(skillId);
    return ringsOwners[baseSkill];
end

------------------------------
-- 当前界面选中(正中心)的飞艇id
function setSelectedAirShip(shipId)
    selectedShipId = shipId;
end

-- 获取当前选中的飞艇id
function getSelectedAirShip()
    return type(selectedShipId) == "number" and selectedShipId or 1;
end

-- 判断飞艇是否激活了副炮平台
function isActiveWingman(shipId)
    local id = 11; -- 副炮搭载平台固定是第11次强化
    local airships = ME.user.dbase:query(AIRSHIP_PATH) or {};
    local airshipInfo = airships[shipId] or {};
    local strengthen = airshipInfo["strengthen"] or {};

    return table.indexOf(strengthen, id) > 0;
end

-- 获取当前派遣的僚机
function getWingman()
    local activeShip = getActiveAirShip();
    local wingman = ME.user.dbase:query("airship_wingman", {});
    return wingman[activeShip] or {};
end

-- 获取飞艇的主炮(包含链接的副炮，并且有序)
function getArmament()
    -- 1 科技炮
    -- 2 特殊炮
    -- 3 魔法炮

    local result = {};
    local wingman = getWingman();

    for _, shipId in ipairs(table.append({ getActiveAirShip() }, wingman)) do
        if shipId > 0 then
            local skill = query(shipId, "physic_skill");
            local skillType = query(shipId, "skill_type");
            local skillLevel = getCannonLevel(shipId);

            result[skillType] = {["ship_id"] = shipId, ["skill_id"] = skill, ["skill_level"] = skillLevel};
        end
    end

    return result;
end

-- 派遣僚机
function dispatch(shipId)
    local isFound = false;
    local skillType = query(shipId, "skill_type");
    local activeShip = getActiveAirShip();
    local airshipWingman = ME.user.dbase:query("airship_wingman", {});
    local wingman = airshipWingman[activeShip] or {};

    if skillType == query(activeShip, "skill_type") then
        return;
    end

    for index, id in ipairs(wingman) do
        -- 找到同一系别的僚机，进行替换
        if query(id, "skill_type") == skillType then
            wingman[index] = shipId;
            isFound = true;
        end
    end

    -- 没有找到同一系别的，那么直接添加
    if not isFound then
        table.insert(wingman, shipId);
    end

    -- 到服务器去派遣
    setWingman(wingman[1] or 0, wingman[2] or 0);

    -- 本地修改下属性
    airshipWingman[activeShip] = wingman;
    ME.user.dbase:set("airship_wingman", airshipWingman);

    EventMgr.fire(event.AIRSHIP_WINGMAN, {
        ["shipId"]  = shipId,
    });
end

-- 设置僚机（副炮）
function setWingman(first, second)
    Operation.cmd_set_wingman(first, second);
end

-- 获取指定主炮类型的的飞艇列表
function getShipBySkillType(skillType)
    local result = {};
    for shipId, shipInfo in pairs(airShipTable) do
        if shipInfo["skill_type"] == skillType then
            table.insert(result, shipId);
        end
    end

    return result;
end

-- 获取已拥的指定主炮类型的飞艇
function getOwnTypeShip(skillType)
    local typeShips = getShipBySkillType(skillType);
    local ownShips = {};

    for _, shipId in pairs(typeShips) do
        if hasOwnShip(shipId) then
            table.insert(ownShips, shipId);
        end
    end

    return ownShips;
end

-- 获取已拥有的飞艇
function getOwnShips()
    local ownList = {};

    local allShips = getAllAirShips();
    for shipId, _ in ipairs(allShips) do
        if hasOwnShip(shipId) then
            table.insert(ownList, shipId);
        end
    end

    return ownList;
end

-- 合成和喂食一起同步
function compoundAndStrengthenDevice(shipId, level)
    -- 如果强化装置足够，则自动进行强化
    if NewAirShipM.isDeviceEnough(shipId, level) then
        -- 自动进行强化
        Operation.cmd_airship_strengthen_new(shipId, level);
        return true;
    end

    if isMaterialEnough(shipId, level) ~= true then
        -- 材料不足
        return false;
    end

    local device = getStrengthenDevice(shipId, level);
    local deviceId = device[2];

    -- 获取合成规则
    local rule = CompoundM.getCompoundRule(deviceId);
    if not rule then
        return false;
    end

    -- 直接合成
    if CompoundM.doCompound(rule, "compound") ~= true  then
        return false;
    end

    -- 客户端合成后再通知服务端
    Operation.cmd_compound_and_strengthen(shipId, level);
end

-- 飞艇能否强化
function canStrengthen(id, level)
    -- 当前强化等级
    local shipInfo = ME.user.dbase:query("new_airships")[id];
    if not shipInfo then
        return false;
    end

    -- 当前最大强化等级
    local maxLevel = getMaxStrengthenLevel(id);

    if maxLevel < level then
        return false;
    end

    -- 都没有强化记录，那么是可以强化的
    local strengthenInfo = shipInfo["strengthen"];
    if strengthenInfo and table.indexOf(strengthenInfo, level) > 0 then
        return false;
    end

    return true;
end

-- 飞艇是否存在剩余的改造项
function hasRemainStrengthenDevice(id)
    -- 当前所有的改造项
    local allStrengthenNum = getShowGroup(id) * GROUP_DEVICE_NUM;

    -- 已改造项
    local record = getStrengthenRecord(id) or {};
    local strengthenNum = #record;

    return strengthenNum ~= allStrengthenNum;
end

-- 飞艇能否进阶
function canUpRank(id)
    -- 已经达到最高星级
    if getShowGroup(id) == MAX_STAR_NUM then
        return false;
    end

    -- 装置条件
    if hasRemainStrengthenDevice(id) then
        return false;
    end

    -- 资源条件
    local rank = getAirShipRank(id);
    local cost = FormulaM.invoke("CALC_AIRSHIP_UPGRADE_COST", id, rank);
    local chipId = query(id, "chip_id");
    if not cost then return false; end
    if ItemM.getAmount(ME.user, chipId) < cost[chipId] then
        return false;
    end

    return true;
end